﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections;
using System.Windows.Forms;

using Noris.Schedule.Support.Services;
using Noris.Schedule.Support.Data;
using Noris.Schedule.Support.Core;
using System.Data;
using Noris.Schedule.Support.Sql;
using Noris.Schedule.Support.DataFace;
using System.Threading;

namespace Noris.Schedule.Support
{
	/// <summary>
	/// Třída Cowley obsahuje podporu pro spouštění velkých neinteraktvních funkcí.
	/// Kterákoli výkonná funkce (potomek ICowley) může zavolat statické metody třídy Cowley.
	/// </summary>
	public abstract class Cowley : ICowley, ICallBackTarget
	{
		#region IMPLEMENTACE ICowley, ABSTRACT PROPERTY + METHOD
		/// <summary>
		/// Zde Cowley vrací svoji identifikaci (číslo, název, cokoliv), jako string.
		/// Podle této identifikace se určuje, která výkonná třída provede konkrétní funkci.
		/// </summary>
		string ICowley.CowleyID { get { return this.CowleyID; } }
		/// <summary>
		/// Vstupní bod Cowley.
		/// Jakékoli parametry (předané v příkazové řádce) si může vyzvednout přes Steward.ParamGet(name).
		/// Pro zobrazování progresu jsou určeny metody Steward.CowleyProgressInit() a Steward.CowleyProgress().
		/// </summary>
		void ICowley.Run() { this.Run(); }
		/// <summary>
		/// Zde Cowley vrací svoji identifikaci (číslo, název, cokoliv), jako string.
		/// Podle této identifikace se určuje, která výkonná třída provede konkrétní funkci.
		/// </summary>
		public abstract string CowleyID { get; }
		/// <summary>
		/// Vstupní bod Cowley.
		/// Jakékoli parametry (předané v příkazové řádce) si může vyzvednout přes Steward.ParamGet(name).
		/// Pro zobrazování progresu jsou určeny metody Steward.CowleyProgressInit() a Steward.CowleyProgress().
		/// </summary>
		public abstract void Run();
		#endregion
		#region IMPLEMENTACE ICallBackTarget : PŘEDÁVÁNÍ INFORMACÍ O PROGRESU DO STEWARDA
		/// <summary>
		/// Metoda, která je volána při jakémkoli požadavku z výkonné vrstvy do volající vrstvy.
		/// Druh požadavku a data jsou specifikována v argumentu.
		/// Jde o změnu progresu, dělení progresu na segmenty, oznámení o dokončení běhu funkce.
		/// </summary>
		/// <param name="sender">Odesílatel události</param>
		/// <param name="args">Data o průběhu/stavu</param>
		void ICallBackTarget.CallTarget(object sender, CallBackTargetArgs args)
		{
			Cowley.CallTarget(args);
		}
		#endregion
		#region PUBLIC STATIC SLUŽBY PRO VÝKONNÉ FUNKCE (Login, Progress)
		/// <summary>
		/// Předá informaci o stavu z výkonného procesu do volajícího procesu
		/// </summary>
		public static void CallTarget(CallBackTargetArgs args)
		{
			ThreadCowley tc = Steward.CowleyThreadFind();
			if (tc != null)
				tc.CallTarget(args);
		}
		/// <summary>
		/// Zajistí vytvoření dalšího segmentu v progresu pro daný rozsah.
		/// Segmenty lze libovolně vnořovat.
		/// Vrácený objekt je IDisposable, je třeba jej užívat v using patternu. Po jeho Dispose končí platnost segmentu.
		/// Uvnitř bloku using je možno volat DoProgress s hodnotou ratio 0 až 1, hodnota bude zobrazovaná v rámci svého segmentu.
		/// Pokud tedy bude nastaven segment 0.35 až 0.90, pak do této části progresu se bude promítat postup vnitřní funkce, která bude zobrazovat hodnoty 0 až 1.
		/// Pokud si ta vnořená funkce vytvoří pro svoji určitou část svůj vlastní segment například pro rozmezí 0.50 až 1.00, 
		/// pak tento vnitřní segment bude fyzicky promítán do hodnot (0.35 + (0.50 * (0.90 - 0.35))) až (0.35 + (1.00 * (0.90 - 0.35))) = 0.625 ( = 1/2 intervalu 0.35 až 0.90) až 0.90.
		/// </summary>
		/// <param name="fromRatio">Počátek tohoto segmentu, v aktuálním prostoru (buď celý progres, nebo aktuálně přidělený segment). Má mít hodnotu 0 až 1.</param>
		/// <param name="toRatio">Konec tohoto segmentu, v aktuálním prostoru (buď celý progres, nebo aktuálně přidělený segment). Má mít hodnotu 0 až 1.</param>
		/// <returns>Objekt segmentu, jehož Dispose ukončuje platnost segmentu.</returns>
		public static ProgressSegment ProgressCreateSegment(decimal fromRatio, decimal toRatio)
		{
			CallBackTargetArgs args = CallBackTargetArgs.CreateSegment(fromRatio, toRatio);
			CallTarget(args);
			return args.ProgrSegment;
		}
		/// <summary>
		/// Podpora pro Cowley: zajistí aktivaci datového profilu podle předaných parametrů z příkazové řádky.
		/// Přebírá parametry: { /Us=číslo subjektu user, /Sc=ConnectString kódovaný anebo /Sd=ConnectString nekódovaný, /D=Jméno profilu, profil nemusí existovat }
		/// anebo parametry { /N=name, /P=password, /D=Datový profil, profil musí existovat v Config }
		/// </summary>
		public static void Login()
		{
			Steward.CowleyLogin();
		}
        /// <summary>
        /// Ověří, zda aktuální uživatel má právo na spouštění dané funkce. Pokud ne, ohlásí chybu (Throw.InputError()).
        /// </summary>
        public static void CheckPermission(int functionNumber)
		{
			Steward.CowleyCheckPermission(functionNumber);
		}
		/// <summary>
		/// Podpora pro Cowley: zajistí deaktivaci datového profilu.
		/// </summary>
		public static void Logout()
		{
			Steward.CowleyLogout();
		}
		#endregion
		#region STATICKÉ METODY PRO SPOUŠTĚNÍ FUNKCE
		/// <summary>
		/// Spuštění funkce, zadané jako parametr této metody
		/// </summary>
		public static void RunCowley(string cowleyId)
		{
			RunCowley(cowleyId, (ICallBackTarget)null);
		}
		/// <summary>
		/// Spuštění funkce, zadané jako parametr této metody
		/// </summary>
		public static void RunCowley(string cowleyId, ICallBackTarget callBackTarget)
		{
			if (String.IsNullOrEmpty(cowleyId))
				Throw.SysError(MessageInfo.Get("Je požadováno spuštění funkce Cowley, ale není specifikováno která funkce se má spouštět (parametr cowleyId nebyl vyplněn)."));

			_RunCowley(cowleyId, callBackTarget);
		}
		/// <summary>
		/// Fyzické nalezení výkonné třídy Cowley pro daný ID, vyvolání spuštění (Steward.CowleyRun(Type);)
		/// </summary>
		/// <param name="cowleyId"></param>
		private static void _RunCowley(string cowleyId, ICallBackTarget callBackTarget)
		{
			IEnumerable<ICowley> cowleys = Steward.GetServiceObjectList<ICowley>(true);

			// Najdu implementátora:
			ICowley cowley = null;
			int totalCnt = 0;
			bool duplicite = false;
			string txt = "";
			foreach (ICowley c in cowleys)
			{
				if (c == null) continue;
				totalCnt++;

				if (String.Equals(c.CowleyID, cowleyId))
				{
					txt += c.GetType().FullName + Environment.NewLine;

					if (cowley == null)
						cowley = c;
					else
						duplicite = true;
				}
			}

			// Zjistím, zda existuje právě jeden implementátor požadované funkce:
			if (totalCnt == 0)
				Throw.InputError(MessageInfo.Get("Je požadováno spuštění funkce \"%0\", ale systém neobsahuje vůbec žádnou funkci.", cowleyId));
			else if (cowley == null)
				Throw.InputError(MessageInfo.Get("Je požadováno spuštění funkce \"%0\", ale systém tuto funkci nenalezl.", cowleyId));
			else if (duplicite)
				Throw.InputError(MessageInfo.Get("Je požadováno spuštění funkce \"%0\", ale v systému se k této funkci hlásí více tříd:\r\n%1", cowleyId, txt));
			else
				Steward.CowleyRun(cowley.GetType(), callBackTarget);
		}
		#endregion
	}
	#region CLASS ThreadCowley : Třída, která poskytuje separátní thread pro běh funkce ICowley.
	/// <summary>
	/// ThreadCowley : Třída, která poskytuje separátní thread pro běh funkce ICowley.
	/// V této třídě je vlákno + Cowley, v tomto vláknu Cowley pracuje.
	/// Cowley (=výkonná třída funkce) sama osobně nemá referenci na tuto třídu (nic jako Parent neexistuje).
	/// Komunikuje se statickými metodami třídy Noris.Schedule.Support.Cowley (řízení progresu, Login, Logout)
	/// </summary>
	internal class ThreadCowley
	{
		/// <summary>
		/// Vytvoření nového threadu pro danou funkci, která poběží na pozadí
		/// </summary>
		/// <param name="cowley"></param>
		internal ThreadCowley(ICowley cowley, ICallBackTarget callBackTarget)
		{
			this._Cowley = cowley;                                  // Uložím referenci na Cowley
			this._WorkingThread = new Thread(this._StartThread);    // Vytvořím nový Thread, ale nerozbíhám ho.
			this._CallBackTarget = callBackTarget;                  // Uložím si referenci na objekt, který je cílem volání CallbackTarget
		}
		/// <summary>
		/// Nastartuje funkci Cowley v separátním threadu.
		/// Aktuální thread se tím nezdrží, vrací řízení okamžitě (v řádku milisekund).
		/// Thread si žije sám svým životem, putuje vesmírem a pamětí počítače.
		/// Až funkce skončí, bude zavolána funkce Steward.CowleyEnd() v jeho separátním threadu
		/// </summary>
		internal void StartThread()
		{
			this._WorkingThread.Start();
			System.Threading.Thread.Sleep(1);            // Tím uvolním vlákno _WorkingThread (dostane čas k rozběhu), a pak řízení opustí Current thread.
		}
		/// <summary>
		/// Unikátní ID threadu
		/// </summary>
		internal int ThreadId { get { return (this._WorkingThread == null ? 0 : this._WorkingThread.ManagedThreadId); } }
		private ICowley _Cowley;
		private Thread _WorkingThread;
		private ICallBackTarget _CallBackTarget;
		/// <summary>
		/// Vstupní bod, který se spouští po startu separátního threadu
		/// </summary>
		private void _StartThread()
		{
			try
			{
				Steward.AuditInfo("Funkce spuštěna");
				this._Cowley.Run();
				this.CallTarget(CallBackTargetArgs.CreateSucceed());
				Steward.Audit(AuditRowType.Completed, "Funkce úspěšně skončila", null);
			}
			catch (Exception exc)
			{
				this.CallTarget(CallBackTargetArgs.CreateFail(exc));
				Steward.Audit(AuditRowType.ApplicationError, "Funkce skončila s chybou", exc);
			}
			finally
			{	// Ať už to skončilo dobře nebo špatně, skončilo to:
				// Odebereme this z fondu vlákem ve Stewardu:
				Steward.CowleyThreadRemove(this);
			}
		}
		/// <summary>
		/// Zajistí vyvolání cíle (CallBackTarget) a předání daného argumentu
		/// </summary>
		/// <param name="args"></param>
		internal void CallTarget(CallBackTargetArgs args)
		{
			if (this._CallBackTarget != null)
				this._CallBackTarget.CallTarget(this._Cowley, args);
		}
		/// <summary>
		/// Obsahuje true, pokud je vyhodnocováno z téhož threadu, ve kterém běží Cowley (tj. odpovídající thread na pozadí).
		/// </summary>
		internal bool IsCurrentThread { get { return this._WorkingThread != null && Object.Equals(System.Threading.Thread.CurrentThread, this._WorkingThread); } }
	}
	#endregion
}
